import configparser
import time

import pygame
from selenium import webdriver
from selenium.common import NoSuchElementException, TimeoutException
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support import expected_conditions as EC
import logging
import colorlog
import random

# TODO 登录需要验证码，手动登录更方便，暂不开发。异常验证目前成本较高，后续再考虑


# 配置日志输出
logger = logging.getLogger()
logger.setLevel(logging.INFO)

handler = colorlog.StreamHandler()
handler.setFormatter(colorlog.ColoredFormatter(
    '%(log_color)s%(asctime)s - %(levelname)s - %(message)s',
    log_colors={
        'DEBUG': 'reset',
        'INFO': 'purple',
        'WARNING': 'yellow',
        'ERROR': 'red',
        'CRITICAL': 'bold_red',
    }
))

logger.addHandler(handler)
# 读取配置文件
config = configparser.ConfigParser()
config.read('config.ini', encoding='utf-8')

# 访问链接
url = config.get('Website', 'url')
# 打招呼的内容
msg_list = config.get('Greeting', 'msg').split(';;')
browser_type = config.get('Browser', 'type')
min_wait_time = config.getint('Timeout', 'min_wait_time')
max_wait_time = config.getint('Timeout', 'max_wait_time')
# 可以包含的关键字列表和不能包含的关键字列表
include_keywords = config.get('Filters', 'IncludeKeywords').split(',')
exclude_keywords = config.get('Filters', 'ExcludeKeywords').split(',')
# 薪资的最低范围
salary_mini = config.get('Salary', 'mini')
salary_max = config.get('Salary', 'max')



# 沟通
def communicate(driver):
    # 成功沟通次数
    successNum = 0
    # 下拉刷新职位信息
    scroll_down(driver)
    # 获取职位列表
    jd_list_items = get_job_list(driver)
    logger.info('共获取到%d个职位', len(jd_list_items))
    for item in jd_list_items:
        # 获取岗位基础信息
        titleText, workSpace, salary = get_job_basics(item)
        salarySlice = salary[0:2]
        # 去除末尾的连字符 "-"
        if salarySlice[-1] == '-':
            salarySlice = salarySlice[:-1]
        matchSalary = salarySlice[-1] == '-' or int(salarySlice) < int(salary_mini) or int(salarySlice) > int(
            salary_max)
        # 对职位进行筛选
        notShenzhen = not ('深圳' in workSpace)
        notCity = not (('深圳' in workSpace) or ('长沙' in workSpace))
        # 判断标题中是否包含任何一个可以包含的关键字
        include_match = any(keyword.lower() in titleText.lower() for keyword in include_keywords)
        # 判断标题中是否包含任何一个不能包含的关键字
        exclude_match = any(keyword.lower() in titleText.lower() for keyword in exclude_keywords)
        # 如果没有匹配到可以包含的关键字，或者匹配上了，但是也有不能包含的内容，跳出后续操作
        if include_match:
            if exclude_match:
                logger.warning("岗位不匹配")
                continue
        else:
            continue
        if notCity or matchSalary:
            logger.warning("薪水或城市不匹配，城市：%s，薪资范围：%s", workSpace, salary)
            continue
        logger.info("准备打开链接")
        link_href = item.find_element(By.TAG_NAME, "a").get_attribute('href')
        driver.execute_script('window.open("{}","_blank");'.format(link_href))
        logger.info("成功打开链接")
        # 聚焦当前 tab 否则找不到元素
        win_handle = driver.window_handles
        logger.info("获取元素")
        driver.switch_to.window(win_handle[1])
        logger.info("移动到窗口【1】")
        try:
            # 1、点击立即沟通
            communicateImmediately(driver)
            # 有可能已经沟通过了，会弹出框框提示已向BOSS发送消息，需要关闭
            # 2、判断是否沟通过
            isCommunicated = hasBeenCommunicated(driver)
            if isCommunicated:
                driver.switch_to.window(win_handle[0])
                logger.info('成功沟通次数：%d', successNum + 1)
                successNum += 1
                continue
            # 3、输入招呼语
            receiveChatMessage(driver)
            # 4、发送消息
            submitMsg(driver)
            # 5、关闭页面
            closePage(driver)
            logger.info('成功沟通次数：%d', successNum + 1)
            successNum += 1
            driver.switch_to.window(win_handle[0])
        except Exception as e:
            # 打印捕获的异常类型和相关信息
            print("捕获到异常:", type(e), str(e))
            WebDriverWait(driver, 10)
            result = exceptionHandling(driver)
            if result:
                return
            # 切换到第一个窗口
            driver.switch_to.window(win_handle[0])
    logger.warning("推荐岗位已沟通完毕")


# 执行下拉操作的代码
def scroll_down(driver):
    # 下拉刷新次数
    refreshmentNum = 0
    read_more = driver.find_element(
        By.XPATH, '//*[@id="main"]/div[3]/div[2]/div')
    while not elementHasClass(read_more, 'disabled'):
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        logger.info("下拉获取更多次数：%d", refreshmentNum + 1)
        refreshmentNum += 1
        time.sleep(2)

    time.sleep(2)


def get_job_list(driver):
    # 获取职位列表的代码
    jd_list = driver.find_element(
        By.XPATH, '//*[@id="main"]/div[3]/div[2]/ul')
    jd_list_items = jd_list.find_elements(By.TAG_NAME, "li")
    return jd_list_items


# 获取岗位基本信息
def get_job_basics(item):
    # 岗位名称
    titleText = item.find_element(By.CLASS_NAME, "title-text").get_attribute('innerText')
    # 工作城市
    workSpace = item.find_element(By.CLASS_NAME, "workplace").get_attribute('innerText')
    # 薪资范围
    salary = item.find_element(By.CLASS_NAME, "salary").get_attribute('innerText')

    return titleText, workSpace, salary


# 1、点击立即沟通
# def communicateImmediately(driver):
#     try:
#         # click事件失效，改为执行js脚本的方式
#         button = driver.find_element(
#             By.XPATH, '//*[@id="main"]/div[3]/div[2]/a')
#         driver.execute_script('$(arguments[0]).click()', button)
#         logger.info('点击立即沟通')
#     except NoSuchElementException:
#         time.sleep(4)
#         try:
#             driver.find_element(By.CLASS_NAME, 'btn-startchat').click()
#             logger.info('手机模式异常，改为pc模式点击立即沟通')
#         except NoSuchElementException:
#             driver.find_element(By.CLASS_NAME, 'btn-chat').click()
#             logger.info('pc模式异常，改为btn-chat点击立即沟通')
#         # logger.info('手机模式异常，改为pc模式点击立即沟通')
#     time.sleep(2)

def communicateImmediately(driver):
    try:
        # 等待立即沟通按钮元素出现，最长等待时间为15秒，可根据网络情况适当调整
        button = WebDriverWait(driver, max_wait_time).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="main"]/div[3]/div[2]/a'))
        )
        driver.execute_script('$(arguments[0]).click()', button)
        logger.info('点击立即沟通')
    except (NoSuchElementException, TimeoutException):
        try:
            button = driver.find_element(By.CLASS_NAME, 'btn-startchat')
            driver.execute_script('$(arguments[0]).click()', button)
            # driver.find_element(By.CLASS_NAME, 'btn-startchat').click()
            logger.info('手机模式异常，改为pc模式点击立即沟通')
        except NoSuchElementException:
            button = driver.find_element(By.CLASS_NAME, 'btn-chat')
            driver.execute_script('$(arguments[0]).click()', button)
            logger.info('pc模式异常，改为btn-chat点击立即沟通')
    # 等待一段时间确保页面稳定
    time.sleep(1)


# 2、是否已经沟通过
def hasBeenCommunicated(driver):
    try:
        driver.find_element(By.CLASS_NAME, "btn-sure")
        # 元素存在
        logger.info("电脑模式，无法继续沟通")
        # driver.execute_script('$(arguments[0]).click()', btn_sure)
        # time.sleep(2)
        driver.close()
        # time.sleep(1)
        return True
        # # 等待确认按钮元素出现，最长等待时间为10秒
        # WebDriverWait(driver, 10).until(
        #     EC.presence_of_element_located((By.CLASS_NAME, "btn-sure"))
        # )
        # logger.info("已经沟通过了，关闭页面进入下一个")
        # # 等待一段时间确保页面稳定
        # time.sleep(1)
        # driver.close()
        # return True
    except NoSuchElementException:
        # 元素不存在
        logger.info("手机模式，继续")
        return False


# 3、打招呼
# def receiveChatMessage(driver):
#     try:
#         # driver.find_element(By.XPATH, '/html/body/div[1]/div[4]/input').send_keys(msg)
#         # logger.info("填充招呼语")
#         # 等待页面中的输入框元素出现，最长等待时间为10秒
#         input_box = WebDriverWait(driver, 10).until(
#             EC.presence_of_element_located((By.XPATH, '/html/body/div[1]/div[4]/input'))
#         )
#         input_box.send_keys(msg)
#         logger.info("填充招呼语")
#     except NoSuchElementException:
#         # time.sleep(2)
#         # driver.find_element(By.CLASS_NAME, 'input-area').send_keys(msg)
#         # logger.info("手机模式异常，改为pc模式填充招呼语")
#         try:
#             # 等待输入框元素出现，最长等待时间为10秒
#             input_box = WebDriverWait(driver, 10).until(
#                 EC.presence_of_element_located((By.CLASS_NAME, 'input-area'))
#             )
#             input_box.send_keys(msg)
#             logger.info("手机模式异常，改为pc模式填充招呼语")
#         except Exception as e:
#             logger.error("填充招呼语页面加载超时或未找到输入框元素:" + str(e))
#     # time.sleep(5)
# 3、打招呼
def receiveChatMessage(driver):
    try:
        # 等待页面中的输入框元素出现，最长等待时间为10秒
        input_box = WebDriverWait(driver, min_wait_time).until(
            EC.presence_of_element_located((By.XPATH, '/html/body/div[1]/div[4]/input'))
        )
        # 随机选择一条消息
        msg = random.choice(msg_list)
        input_box.send_keys(msg)
        logger.info("填充招呼语")

        # 等待输入框中的文本与招呼语一致，最长等待时间为10秒
        WebDriverWait(driver, min_wait_time).until(
            lambda driver1: input_box.get_attribute('value') == msg
        )
        logger.info("招呼语已成功填充到输入框")
    except Exception as e:
        try:
            # 等待输入框元素出现，最长等待时间为10秒
            input_box = WebDriverWait(driver, min_wait_time).until(
                EC.presence_of_element_located((By.CLASS_NAME, 'input-area'))
            )
            # 随机选择一条消息
            msg = random.choice(msg_list)
            input_box.send_keys(msg)
            logger.info("手机模式异常，改为pc模式填充招呼语")

            # 等待输入框中的文本与招呼语一致，最长等待时间为10秒
            WebDriverWait(driver, min_wait_time).until(
                lambda driver1: input_box.get_attribute('value') == msg
            )
            logger.info("招呼语已成功填充到输入框")
        except Exception as e:
            logger.error("填充招呼语页面加载超时或未找到输入框元素:" + str(e))


# 4、点击发送按钮，发送消息
# def submitMsg(driver):
#     try:
#         # 等待发送按钮元素可点击，最长等待时间为10秒
#         button = WebDriverWait(driver, 10).until(
#             EC.element_to_be_clickable((By.XPATH, '/html/body/div[1]/div[4]/button'))
#         )
#         button.click()
#         logger.info('点击发送')
#     except TimeoutException:
#         try:
#             # 等待发送按钮元素可点击，最长等待时间为10秒
#             button = WebDriverWait(driver, 10).until(
#                 EC.element_to_be_clickable((By.CLASS_NAME, 'send-message'))
#             )
#             driver.execute_script('$(arguments[0]).click()', button)
#             logger.info('手机模式异常，改为pc模式，换个方式点击发送')
#         except Exception as e:
#             logger.error("发送消息页面加载超时或未找到发送按钮元素:" + str(e))


# 4、点击发送按钮，发送消息
def submitMsg(driver):
    try:
        # 这里的点击事件失效了，此处没有删除的原因是在pc模式下find_element才能触发异常
        driver.find_element(
            By.XPATH, '/html/body/div[1]/div[4]/button').click()
        # 通过js代码来实现点击（nth-of-type(1)是CSS伪类选择器中的一种，用于选择特定类型的元素中的第一个元素）
        js = "document.querySelector('html > body > div:nth-of-type(1) > div:nth-of-type(4) > button').click()"
        driver.execute_script(js)
        # driver.execute_script('$(arguments[0]).click()', button)
        logger.info('点击发送')
    except NoSuchElementException:
        button = driver.find_element(
            By.CLASS_NAME, 'send-message')
        driver.execute_script('$(arguments[0]).click()', button)
        logger.info('手机模式异常，改为pc模式，换个方式点击发送')


# 5、关闭页面
def closePage(driver):
    time.sleep(2)
    driver.close()
    time.sleep(1)


# 异常处理方法
def exceptionHandling(driver):
    # 已达上限处理方法
    try:
        driver.find_element(By.XPATH, "//h4[text()='无法进行沟通']")
        logger.info("今日沟通已达上限")
        return True
    except NoSuchElementException:
        pass
    # 播放声音
    play_sound(driver)
    # 关闭页面
    closePage(driver)
    return False


def elementHasClass(element, active):
    class_str = element.get_attribute('class')
    return active in class_str


def create_driver():
    driver = webdriver.Chrome(options=get_options(), service=get_service())
    driver.set_window_size(415, 1100)
    driver.implicitly_wait(2)
    time.sleep(2)
    driver.get(url)
    communicate(driver=driver)
    return driver


def get_options():
    options = webdriver.ChromeOptions()
    options.add_experimental_option('detach', True)  # 不自动关闭浏览器
    mobile_emulation = {"deviceName": "Nexus 6"}
    # {'deviceName': 'iPhone XR'}
    options.add_experimental_option('mobileEmulation', mobile_emulation)
    # 设置 user-data-dir chrome会加载此配置 里面包含了用户的 cookie
    # 如果自定义的user-data-dir文件夹 登录获取验证会失败 暂时使用第一次启动的文件夹 通过 chrome://version 查看目录 保持登录状态
    # options.add_argument(
    #     "user-data-dir=C:\Users\jidussy\AppData\Local\Google\Chrome\User Data")
    options.add_argument("user-data-dir=C:\\Users\\jidussy\\AppData\\Local\\Google\\Chrome\\User Data")
    # 获取本地提前启动好的chrome调试服务，之后打开同一个实例，否则每次打开都是新的
    options.add_argument("--remote-debugging-port=9231")
    # 指定浏览器启动位置
    # options.binary_location = 'D:\\Application\\chrome-win64\\chrome.exe'
    return options


def get_service():
    driver_manager = ChromeDriverManager()
    driver_path = driver_manager.install()
    # 创建 Service 实例
    service = Service(driver_path)
    return service


def play_sound(driver):
    # 初始化 pygame
    pygame.init()

    # 设置音频设备
    pygame.mixer.init()

    # 加载音频文件
    sound = pygame.mixer.Sound('./static/遇到问题的语音.mp3')
    # 判断是否为触发了验证，其他异常则直接关闭页面进行下一个
    try:
        driver.find_element(By.CLASS_NAME, "page-verify-slider")
        # 播放3次音频
        sound.play(loops=2)
        input("触发验证操作，请操作完后按下 Enter 键继续...")
    except NoSuchElementException:
        pass

    # 停止播放音频
    sound.stop()
    # 等待音频播放完毕
    while pygame.mixer.get_busy():
        pass

    # 关闭 pygame
    pygame.quit()


if __name__ == '__main__':
    create_driver()
